#include <rtdevice.h>
#include <board.h>
#include <OLED.h>
#include <OLED_Data.h>
#include <string.h>

#define OLED_I2C_BUS_NAME "i2c1"
#define OLED_ADDR 0x3c //从机地址 0011 1100 原来是0x78 记得右移1位
uint8_t OLED_DisplayBuf[8][128];
struct rt_i2c_bus_device *i2c_bus=RT_NULL;

static rt_err_t WriteCmd(struct rt_i2c_bus_device *bus,uint8_t data)
{
    rt_uint8_t buf[2];
    struct rt_i2c_msg msgs;

    buf[0] = 0x00;
    buf[1] = data;


    msgs.addr = OLED_ADDR;
    msgs.flags = RT_I2C_WR;
    msgs.buf = buf;
    msgs.len = 2;

    /* 调用I2C设备接口传输数据 */
    if (rt_i2c_transfer(bus, &msgs, 1) == 1)
    {
        return RT_EOK;
    }
    else
    {
        return -RT_ERROR;
    }
}

//static rt_err_t WriteDat(struct rt_i2c_bus_device *bus, rt_uint8_t *data,uint8_t Count)
//{
//    rt_uint8_t buf[256];
//    struct rt_i2c_msg msgs;
//
//    buf[0]=0x40;
//
//    memcmp(buf+1, data, Count);
//    msgs.addr = OLED_ADDR;
//    msgs.flags = RT_I2C_WR;
//    msgs.buf = buf;
//    msgs.len = Count+1;
//
//    /* 调用I2C设备接口传输数据 */
//    if (rt_i2c_transfer(bus, &msgs, 1) == 1)
//    {
//        return RT_EOK;
//    }
//    else
//    {
//        return -RT_ERROR;
//    }
//}
static rt_err_t WriteDat(struct rt_i2c_bus_device *bus, rt_uint8_t *data,rt_uint8_t Count)
{
    rt_uint8_t buf[512];
    uint8_t i;
    struct rt_i2c_msg msgs;
    rt_uint32_t buf_size = 1;

    buf[0] = 0x40; //cmd
    if (data != RT_NULL)
    {
        for(i=1;i<=Count;i++)
            buf[i] = data[i-1];
        buf_size = Count;
    }

    msgs.addr = OLED_ADDR;
    msgs.flags = RT_I2C_WR;
    msgs.buf = buf;
    msgs.len = buf_size;

    /* 调用I2C设备接口传输数据 */
    if (rt_i2c_transfer(bus, &msgs, 1) == 1)
    {
        return RT_EOK;
    }
    else
    {
        return -RT_ERROR;
    }
}

void OLED_SetCursor(uint8_t Page, uint8_t X)
{
    /*如果使用此程序驱动1.3寸的OLED显示屏，则需要解除此注释*/
    /*因为1.3寸的OLED驱动芯片（SH1106）有132列*/
    /*屏幕的起始列接在了第2列，而不是第0列*/
    /*所以需要将X加2，才能正常显示*/
//  X += 2;

    /*通过指令设置页地址和列地址*/
    WriteCmd(i2c_bus,0xB0 | Page);                 //设置页位置
    WriteCmd(i2c_bus,0x10 | ((X & 0xF0) >> 4));    //设置X位置高4位
    WriteCmd(i2c_bus,0x00 | (X & 0x0F));           //设置X位置低4位
}

void OLED_Update(void)
{
    uint8_t j;
    /*遍历每一页*/
    for (j = 0; j < 8; j ++)
    {
        /*设置光标位置为每一页的第一列*/
        OLED_SetCursor(j, 0);
        /*连续写入128个数据，将显存数组的数据写入到OLED硬件*/
        WriteDat(i2c_bus,OLED_DisplayBuf[j], 128);
    }
}

/**
  * 函    数：将OLED显存数组全部清零
  * 参    数：无
  * 返 回 值：无
  * 说    明：调用此函数后，要想真正地呈现在屏幕上，还需调用更新函数
  */
void OLED_Clear(void)
{
    uint8_t i, j;
    for (j = 0; j < 8; j ++)                //遍历8页
    {
        for (i = 0; i < 128; i ++)          //遍历128列
        {
            OLED_DisplayBuf[j][i] = 0x00;   //将显存数组数据全部清零
        }
    }
}
void OLED_ClearArea(int16_t X, int16_t Y, uint8_t Width, uint8_t Height)
{
    int16_t i, j;

    for (j = Y; j < Y + Height; j ++)       //遍历指定页
    {
        for (i = X; i < X + Width; i ++)    //遍历指定列
        {
            if (i >= 0 && i <= 127 && j >=0 && j <= 63)             //超出屏幕的内容不显示
            {
                OLED_DisplayBuf[j / 8][i] &= ~(0x01 << (j % 8));    //将显存数组指定数据清零
            }
        }
    }
}
void OLED_INIT(void)
{
    rt_thread_mdelay(100);

        WriteCmd(i2c_bus,0xAE);    //关闭显示

        WriteCmd(i2c_bus,0xD5);    //设置显示时钟分频比/振荡器频率
        WriteCmd(i2c_bus,0x80);

        WriteCmd(i2c_bus,0xA8);    //设置多路复用率
        WriteCmd(i2c_bus,0x3F);

        WriteCmd(i2c_bus,0xD3);    //设置显示偏移
        WriteCmd(i2c_bus,0x00);

        WriteCmd(i2c_bus,0x40);    //设置显示开始行

        WriteCmd(i2c_bus,0xA1);    //设置左右方向，0xA1正常 0xA0左右反置

        WriteCmd(i2c_bus,0xC8);    //设置上下方向，0xC8正常 0xC0上下反置

        WriteCmd(i2c_bus,0xDA);    //设置COM引脚硬件配置
        WriteCmd(i2c_bus,0x12);

        WriteCmd(i2c_bus,0x81);    //设置对比度控制
        WriteCmd(i2c_bus,0xCF);

        WriteCmd(i2c_bus,0xD9);    //设置预充电周期
        WriteCmd(i2c_bus,0xF1);

        WriteCmd(i2c_bus,0xDB);    //设置VCOMH取消选择级别
        WriteCmd(i2c_bus,0x30);

        WriteCmd(i2c_bus,0xA4);    //设置整个显示打开/关闭

        WriteCmd(i2c_bus,0xA6);    //设置正常/倒转显示

        WriteCmd(i2c_bus,0x8D);    //设置充电泵
        WriteCmd(i2c_bus,0x14);

        WriteCmd(i2c_bus,0xAF);    //开启显示

        OLED_Clear();               //清空显存数组
        OLED_Update();              //更新显示，清屏，防止初始化后未显示内容时花屏
}


void OLED_ShowImage(int16_t X, int16_t Y, uint8_t Width, uint8_t Height, const uint8_t *Image)
{
    uint8_t i = 0, j = 0;
    int16_t Page, Shift;

    /*将图像所在区域清空*/
    OLED_ClearArea(X, Y, Width, Height);

    /*遍历指定图像涉及的相关页*/
    /*(Height - 1) / 8 + 1的目的是Height / 8并向上取整*/
    for (j = 0; j < (Height - 1) / 8 + 1; j ++)
    {
        /*遍历指定图像涉及的相关列*/
        for (i = 0; i < Width; i ++)
        {
            if (X + i >= 0 && X + i <= 127)     //超出屏幕的内容不显示
            {
                /*负数坐标在计算页地址和移位时需要加一个偏移*/
                Page = Y / 8;
                Shift = Y % 8;
                if (Y < 0)
                {
                    Page -= 1;
                    Shift += 8;
                }

                if (Page + j >= 0 && Page + j <= 7)     //超出屏幕的内容不显示
                {
                    /*显示图像在当前页的内容*/
                    OLED_DisplayBuf[Page + j][X + i] |= Image[j * Width + i] << (Shift);
                }

                if (Page + j + 1 >= 0 && Page + j + 1 <= 7)     //超出屏幕的内容不显示
                {
                    /*显示图像在下一页的内容*/
                    OLED_DisplayBuf[Page + j + 1][X + i] |= Image[j * Width + i] >> (8 - Shift);
                }
            }
        }
    }
}

void OLED_ShowChar(int16_t X, int16_t Y, char Char, uint8_t FontSize)
{
    if (FontSize == OLED_8X16)      //字体为宽8像素，高16像素
    {
        /*将ASCII字模库OLED_F8x16的指定数据以8*16的图像格式显示*/
        OLED_ShowImage(X, Y, 8, 16, OLED_F8x16[Char - ' ']);
    }
    else if(FontSize == OLED_6X8)   //字体为宽6像素，高8像素
    {
        /*将ASCII字模库OLED_F6x8的指定数据以6*8的图像格式显示*/
        OLED_ShowImage(X, Y, 6, 8, OLED_F6x8[Char - ' ']);
    }
}
